from django import forms
from utils.form import BaseForm
from utils.field import ListField
from interface_test_app.constant import Method, Protocol, DataType, AssertRelation
from .models import CaseSetModel, CaseModel
from config_app.models import ProjectModel
import json


# todo 自定义字段的验证，需要注意若该字段未传时触发的bug，required验证在所有验证结束后才放到error中统一返回


def _to_json(obj):
    return json.dumps(obj, ensure_ascii=False, separators=(',', ':'))


class _ReqInfoForm(BaseForm):
    # 请求头,params,form_data
    key = forms.CharField()
    value = forms.CharField()

    def err(self):
        if self.errors:
            return _to_json(dict(self.errors))


class _AssertInfoForm(BaseForm):
    # 断言详情 字段
    expr = forms.CharField()  # 表达式
    type = forms.CharField()  # 期望值类型
    relation = forms.CharField()  # 断言关系
    expectation = forms.CharField(required=False)  # 期望值

    def _check_constant(self, data):
        _ = data.keys()
        type, relation = data['type'] if 'type' in _ else None, data['relation'] if 'relation' in _ else None
        if type and type not in DataType._value:
            self.add_error('type', 'should be one of {}'.format(str(DataType())))
        if relation and relation not in AssertRelation._value:
            self.add_error('relation', 'should be one of {}'.format(str(AssertRelation())))

    def err(self):
        if self.errors:
            return _to_json(dict(self.errors))

    def clean(self):
        self._check_constant(self.cleaned_data)
        self.default_StrField_value(self.cleaned_data, ['expectation'])


class CaseForm:
    class Create(BaseForm):
        name = forms.CharField()
        desc = forms.CharField()
        server = forms.CharField(required=False)
        protocol = forms.CharField()
        url = forms.CharField(required=False)
        method = forms.CharField()
        project = forms.IntegerField()

        headers = ListField(required=False)

        param_data = ListField(required=False)
        form_data = ListField(required=False)
        json_data = ListField(required=False)

        assert_status = ListField(required=False)
        assert_json = ListField(required=False)
        assert_headers = ListField(required=False)

        def _check_project(self, data):
            pro_id = data['project'] if 'project' in data.keys() else None
            if pro_id:
                qs = ProjectModel.objects.filter(id=pro_id)
                if not qs:
                    self.add_error('project', 'invalid project')
                else:
                    data['project'] = qs[0]

        def _check_constant(self, data):
            # 验证常量字段是否合法
            protocol = data['protocol'] if 'protocol' in data.keys() else None
            method = data['method'] if 'method' in data.keys() else None
            if method and (method not in Method._value):
                self.add_error('method', 'should be one of {}'.format(str(Method())))
            if protocol and (protocol not in Protocol._value):
                self.add_error('protocol', 'should be one of {}'.format(str(Protocol())))

        def _validation(self, field, validate_form):
            # 验证子表单
            field_value = self.cleaned_data[field] if field in self.cleaned_data.keys() else None
            if field_value:
                for ele in field_value:
                    if not isinstance(ele, dict):
                        self.add_error(field, 'the element of {} must be an object'.format(field))
                        break
                    son_form = validate_form(ele)
                    if not son_form.is_valid():
                        self.add_error(field, son_form.err())
                        break

        def _set_operate(self, data):
            return self.set_operate_user(data, 'create_by')

        def clean(self):
            self._check_constant(self.cleaned_data)
            [self._validation(field, _ReqInfoForm) for field in [
                'headers', 'param_data', 'form_data', 'json_data'
            ]]  # ReqInfo字段验证

            [self._validation(field, _AssertInfoForm) for field in [
                'assert_status', 'assert_json', 'assert_headers'
            ]]  # AssertInfo字段验证
            data = {}
            for k, v in self.cleaned_data.items():
                if v != None:
                    data.setdefault(k, v)
            data = self._set_operate(data)
            self.default_StrField_value(data, ['server', 'url'])
            self._check_project(data)
            return data

    class Update(Create):
        id = forms.IntegerField()

        def _set_operate(self, data):
            return self.set_operate_user(data, 'update_by')

    class Delete(BaseForm):
        id = forms.IntegerField()

    class List(BaseForm):
        page = forms.IntegerField()
        page_size = forms.IntegerField()

    class Detail(BaseForm):
        id = forms.IntegerField()

    class DebugRecord(BaseForm):
        id = forms.IntegerField()

    class Copy(BaseForm):
        id = forms.IntegerField()


class CaseSetForm:
    class Create(BaseForm):
        name = forms.CharField()
        project = forms.IntegerField()

        def get_project(self, data):
            if 'project' in data.keys():
                pro_id = data['project']
                qs = ProjectModel.objects.filter(id=pro_id)
                if not qs:
                    self.add_error('project', 'invalid project')
                else:
                    data['project'] = qs[0]

        def clean(self):
            data = self.cleaned_data
            self.get_project(data)
            self.set_operate_user(data, 'create_by')
            return data

    class Update(Create):
        id = forms.IntegerField()

    class Delete(BaseForm):
        id = forms.IntegerField()

    class List(BaseForm):
        page = forms.IntegerField()
        page_size = forms.IntegerField()

    class Register(BaseForm):
        cases = ListField()
        set_id = forms.IntegerField()

        def _case(self, data):
            result = []
            for c in data['cases']:
                qs = CaseModel.objects.filter(id=c)
                if not qs:
                    self.add_error('cases', 'invalid case')
                else:
                    result.append(qs[0])
            data['cases_obj'] = result

        def _set(self, data):
            qs = CaseSetModel.objects.filter(id=data['set_id'])
            if not qs:
                self.add_error('set_id', 'invalid set')
            else:
                data['set_obj'] = qs[0]

        def clean(self):
            data = self.cleaned_data
            self._case(data)
            self._set(data)
            return data

    class CurrentSetCases(BaseForm):
        id = forms.IntegerField()


class PubParamForm:
    class Insert(BaseForm):
        name = forms.CharField()
        value = forms.CharField()
        project = forms.IntegerField()

        def clean(self):
            data = self.cleaned_data
            pro_id = data['project']
            qs = ProjectModel.objects.filter(id=pro_id)
            if not qs:
                self.add_error('project', 'invalid project')
            else:
                data['project'] = qs[0]

    class Update(Insert):
        id = forms.IntegerField()

    class Delete(BaseForm):
        id = forms.IntegerField()

    class List(BaseForm):
        page = forms.IntegerField()
        page_size = forms.IntegerField()


class ExecuteForm:
    class Case(BaseForm):
        id = forms.IntegerField()
        env_id = forms.IntegerField()

    class CaseSet(BaseForm):
        id = forms.IntegerField()
        env_id = forms.IntegerField()


class ReportForm:
    class List(BaseForm):
        page = forms.IntegerField()
        page_size = forms.IntegerField()

    class Delete(BaseForm):
        id = forms.IntegerField()

    class Details(BaseForm):
        id = forms.IntegerField()
